//
//  PythonManager.m
//  ios-python
//
//  Created by mac on 2024/12/11.
//

#import "PythonManager.h"



@implementation PythonManager

void crash_dialog(NSString *details) {
    NSLog(@"Application has crashed!");
    NSLog(@"========================\n%@", details);
}

+ (void)initializePythonInterpreterWithArgc {
    NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
    
    PyStatus status;
    PyPreConfig preconfig;
    PyConfig config;
    
    PyPreConfig_InitIsolatedConfig(&preconfig);
    PyConfig_InitIsolatedConfig(&config);
    
    preconfig.utf8_mode = 1;
    config.buffered_stdio = 0;
    config.write_bytecode = 0;
    config.module_search_paths_set = 1;
    
    status = Py_PreInitialize(&preconfig);
    if (PyStatus_Exception(status)) {
        crash_dialog([NSString stringWithFormat:@"Unable to pre-initialize Python interpreter: %s", status.err_msg, nil]);
        PyConfig_Clear(&config);
        Py_ExitStatusException(status);
    }
    
    NSString *python_tag = @"3.11";//根据你的python版本来
    NSString *python_home = [NSString stringWithFormat:@"%@/python", resourcePath, nil];
    NSLog(@"PythonHome: %@", python_home);
    wchar_t *wtmp_str = Py_DecodeLocale([python_home UTF8String], NULL);
    status = PyConfig_SetString(&config, &config.home, wtmp_str);
    if (PyStatus_Exception(status)) {
        crash_dialog([NSString stringWithFormat:@"Unable to set PYTHONHOME: %s", status.err_msg, nil]);
        PyConfig_Clear(&config);
        Py_ExitStatusException(status);
    }
    PyMem_RawFree(wtmp_str);
    
    status = PyConfig_Read(&config);
    if (PyStatus_Exception(status)) {
        crash_dialog([NSString stringWithFormat:@"Unable to read site config: %s", status.err_msg, nil]);
        PyConfig_Clear(&config);
        Py_ExitStatusException(status);
    }
    NSLog(@"PYTHONPATH:");
    NSString *path = [NSString stringWithFormat:@"%@/lib/python%@", python_home, python_tag, nil];
    NSLog(@"- %@", path);
    
    wtmp_str = Py_DecodeLocale([path UTF8String], NULL);
    status = PyWideStringList_Append(&config.module_search_paths, wtmp_str);
    if (PyStatus_Exception(status)) {
        crash_dialog([NSString stringWithFormat:@"Unable to set unpacked form of stdlib path: %s", status.err_msg, nil]);
        PyConfig_Clear(&config);
        Py_ExitStatusException(status);
    }
    PyMem_RawFree(wtmp_str);
    
    path = [NSString stringWithFormat:@"%@/lib/python%@/lib-dynload", python_home, python_tag, nil];
    NSLog(@"- %@", path);
    wtmp_str = Py_DecodeLocale([path UTF8String], NULL);
    status = PyWideStringList_Append(&config.module_search_paths, wtmp_str);
    if (PyStatus_Exception(status)) {
        crash_dialog([NSString stringWithFormat:@"Unable to set unpacked form of stdlib path: %s", status.err_msg, nil]);
        PyConfig_Clear(&config);
        Py_ExitStatusException(status);
    }
    PyMem_RawFree(wtmp_str);
    
    path = [NSString stringWithFormat:@"%@/app_packages", resourcePath, nil];
    NSLog(@"- %@", path);
    wtmp_str = Py_DecodeLocale([path UTF8String], NULL);
    status = PyWideStringList_Append(&config.module_search_paths, wtmp_str);
    if (PyStatus_Exception(status)) {
        crash_dialog([NSString stringWithFormat:@"Unable to set app packages path: %s", status.err_msg, nil]);
        PyConfig_Clear(&config);
        Py_ExitStatusException(status);
    }
    PyMem_RawFree(wtmp_str);
    
    
    path = [NSString stringWithFormat:@"%@/python_scripts.bundle", resourcePath, nil];
    NSLog(@"python_scripts- %@", path);
    wtmp_str = Py_DecodeLocale([path UTF8String], NULL);
    status = PyWideStringList_Append(&config.module_search_paths, wtmp_str);
    if (PyStatus_Exception(status)) {
        crash_dialog([NSString stringWithFormat:@"Unable to set app path: %s", status.err_msg, nil]);
        PyConfig_Clear(&config);
        Py_ExitStatusException(status);
    }
    PyMem_RawFree(wtmp_str);

    NSLog(@"Initializing Python runtime...");
    status = Py_InitializeFromConfig(&config);
    if (PyStatus_Exception(status)) {
        crash_dialog([NSString stringWithFormat:@"Unable to initialize Python interpreter: %s", status.err_msg, nil]);
        PyConfig_Clear(&config);
        Py_ExitStatusException(status);
    }
}

+ (void)finalizePythonInterpreter {
    Py_Finalize();
    NSLog(@"Python interpreter finalized.");
}

+ (id)runPythonScript:(NSString *)scriptName
           withMethod:(NSString *)methodName
              andArgs:(NSArray *)args {
    if (!scriptName || !methodName) {
        NSLog(@"Script name or method name cannot be nil.");
        return nil;
    }
    
    PyObject *pyModule = NULL;
    PyObject *pyFunc = NULL;
    PyObject *pyArgs = NULL;
    PyObject *pyResult = NULL;
    
    @try {
        pyModule = PyImport_ImportModule([scriptName UTF8String]);
        if (!pyModule) {
            PyErr_Print();
            NSLog(@"Failed to import Python module: %@", scriptName);
            return nil;
        }
        
        pyFunc = PyObject_GetAttrString(pyModule, [methodName UTF8String]);
        if (!pyFunc || !PyCallable_Check(pyFunc)) {
            PyErr_Print();
            NSLog(@"Python method %@ is not callable or doesn't exist in module %@", methodName, scriptName);
            return nil;
        }
        
        if (args.count > 0) {
            pyArgs = PyTuple_New(args.count);
            for (NSInteger i = 0; i < args.count; i++) {
                id arg = args[i];
                PyObject *pyArg = NULL;

                if ([arg isKindOfClass:[NSString class]]) {
                    pyArg = PyUnicode_FromString([(NSString *)arg UTF8String]);
                } else if ([arg isKindOfClass:[NSNumber class]]) {
                    if (strcmp([arg objCType], @encode(int)) == 0) {
                        pyArg = PyLong_FromLong([(NSNumber *)arg intValue]);
                    } else if (strcmp([arg objCType], @encode(double)) == 0) {
                        pyArg = PyFloat_FromDouble([(NSNumber *)arg doubleValue]);
                    } else if (strcmp([arg objCType], @encode(NSInteger)) == 0) {
                        pyArg = PyLong_FromLong([(NSNumber *)arg integerValue]);
                    }
                }

                if (pyArg) {
                    PyTuple_SetItem(pyArgs, i, pyArg); // Ownership transferred to tuple
                }
            }
        }
        
        pyResult = PyObject_CallObject(pyFunc, pyArgs);
        if (!pyResult) {
            PyErr_Print();
            NSLog(@"Failed to call Python method %@ in script %@", methodName, scriptName);
            return nil;
        }
        
        if (PyUnicode_Check(pyResult)) {
            return [NSString stringWithUTF8String:PyUnicode_AsUTF8(pyResult)];
        } else if (PyLong_Check(pyResult)) {
            return [NSNumber numberWithLong:PyLong_AsLong(pyResult)];
        } else if (PyFloat_Check(pyResult)) {
            return [NSNumber numberWithDouble:PyFloat_AsDouble(pyResult)];
        }
        
        return nil;
    }
    @finally {
        Py_XDECREF(pyModule);
        Py_XDECREF(pyFunc);
        Py_XDECREF(pyArgs);
        Py_XDECREF(pyResult);
    }
}


@end

